import pickle
import numpy as np
import pandas as pd
from scipy.sparse import load_npz
from config import Config
from .utils import get_popular_products
import os

class MLRecommender:
    """机器学习推荐器"""

    def __init__(self):
        self.models = {}
        self.user_features = None
        self.item_features = None
        self.interaction_matrix = None
        self.user_id_map = None
        self.item_id_map = None
        self.reverse_item_map = None

        self.load_models()

    def load_models(self):
        """加载模型和特征"""
        try:
            # 加载混合模型
            with open(Config.HYBRID_MODEL_FILE, 'rb') as f:
                hybrid_model = pickle.load(f)

            self.models['als'] = hybrid_model['als']
            self.models['content'] = hybrid_model['content']
            self.user_features = hybrid_model['user_features']
            self.item_features = hybrid_model['item_features']

            # 加载交互矩阵和映射
            self.interaction_matrix = load_npz(Config.INTERACTION_MATRIX_FILE)
            self.user_id_map = pd.read_pickle(os.path.join(Config.PROCESSED_DATA_DIR, "user_id_map.pkl")).to_dict()
            self.item_id_map = pd.read_pickle(os.path.join(Config.PROCESSED_DATA_DIR, "item_id_map.pkl")).to_dict()

            # 创建反向映射
            self.reverse_item_map = {v: k for k, v in self.item_id_map.items()}

            # 加载原始商品数据
            self.products = pd.read_csv(Config.PRODUCTS_FILE)

            print("Models and data loaded successfully")
        except Exception as e:
            print(f"Error loading models: {e}")
            raise

    def als_recommend(self, user_id, n=10):
        """使用ALS模型推荐"""
        if user_id not in self.user_id_map:
            return self.cold_start_recommend(n)

        user_idx = self.user_id_map[user_id]
        item_ids, scores = self.models['als'].recommend(
            user_idx,
            self.interaction_matrix[user_idx],
            N=n,
            filter_already_liked_items=True
        )

        # 转换为实际商品ID
        product_ids = [self.reverse_item_map[idx] for idx in item_ids]
        return product_ids, scores

    def content_recommend(self, user_id, n=10):
        """使用内容模型推荐"""
        if user_id not in self.user_features['user_id'].values:
            return self.cold_start_recommend(n)

        # 获取用户特征
        user_data = self.user_features[self.user_features['user_id'] == user_id]

        # 为所有商品生成特征
        all_items = self.item_features.copy()
        all_items['user_id'] = user_id

        # 合并用户特征到所有商品
        merged = all_items.merge(user_data, on='user_id', suffixes=('_item', '_user'))

        # 准备预测数据
        features = merged.drop(['user_id', 'product_id'], axis=1)

        # 预测
        predictions = self.models['content'].predict(features)
        merged['score'] = predictions

        # 获取TopN推荐
        top_items = merged.nlargest(n, 'score')
        return top_items['product_id'].tolist(), top_items['score'].tolist()

    def hybrid_recommend(self, user_id, n=10, als_weight=0.6, content_weight=0.4):
        """混合推荐"""
        # ALS推荐
        als_items, als_scores = self.als_recommend(user_id, n * 3)
        als_df = pd.DataFrame({
            'product_id': als_items,
            'als_score': als_scores
        })

        # 内容推荐
        content_items, content_scores = self.content_recommend(user_id, n * 3)
        content_df = pd.DataFrame({
            'product_id': content_items,
            'content_score': content_scores
        })

        # 合并结果
        combined = pd.merge(als_df, content_df, on='product_id', how='outer').fillna(0)

        # 计算混合分数
        combined['hybrid_score'] = (
                als_weight * combined['als_score'] +
                content_weight * combined['content_score']
        )

        # 获取TopN推荐
        top_items = combined.nlargest(n, 'hybrid_score')
        return top_items['product_id'].tolist(), top_items['hybrid_score'].tolist()

    def cold_start_recommend(self, n=10):
        """冷启动推荐策略"""
        strategy = Config.COLD_START_STRATEGY

        if strategy == "popular":
            # 返回热门商品
            popular = get_popular_products(self.products, n)
            return popular['product_id'].tolist(), [1.0] * n
        elif strategy == "random":
            # 随机推荐
            random_items = self.products.sample(n)
            return random_items['product_id'].tolist(), [0.5] * n
        else:  # content
            # 基于内容的冷启动
            # 这里简化实现，实际中可以使用平均用户特征
            avg_user = self.user_features.mean().to_frame().T
            avg_user['user_id'] = "average"

            # 为所有商品生成特征
            all_items = self.item_features.copy()
            all_items['user_id'] = "average"

            # 合并
            merged = all_items.merge(avg_user, on='user_id', suffixes=('_item', '_user'))
            features = merged.drop(['user_id', 'product_id'], axis=1)

            # 预测
            predictions = self.models['content'].predict(features)
            merged['score'] = predictions

            # 获取TopN
            top_items = merged.nlargest(n, 'score')
            return top_items['product_id'].tolist(), top_items['score'].tolist()

    def get_recommendations(self, user_id, n=10, model_type="hybrid"):
        """获取推荐结果"""
        if model_type == "als":
            item_ids, scores = self.als_recommend(user_id, n)
        elif model_type == "content":
            item_ids, scores = self.content_recommend(user_id, n)
        else:
            item_ids, scores = self.hybrid_recommend(user_id, n)

        # 添加商品详情
        recommendations = self.products[self.products['product_id'].isin(item_ids)]
        recommendations = recommendations.merge(
            pd.DataFrame({'product_id': item_ids, 'score': scores}),
            on='product_id'
        )

        # 按分数排序
        recommendations = recommendations.sort_values('score', ascending=False)
        return recommendations